// 2023-0918

use std::ops::Deref;

use common_uu::IResult;
use crate::{a2_month, b1_date_utils, a1_year::IYear};


/// for short: y=year, m=month, d=day
/// see more: date_utils.rs
#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, Default, PartialEq, Eq)]
pub struct DateFT {
    year: i32,
    month: u8,
    day: u8, 
}

impl DateFT {
    pub fn new() -> Self {
        Self {
            year: 0,
            month: 1,
            day: 1,
        }
    }
    /// please maker sure: month∈[1, 12] && day∈[1, days_inmonth];
    /// in leap year: days_inmonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
    /// in non-leap year: days_inmonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    /// otherwise, an error will be throw.
    pub fn from_ymd(year: i32, month: u8, day: u8,) -> IResult<Self> {
        if month == 0 || month > 12 {
            Err(format!("DateFT::from_ymd: month range must be [1, 12]"))?;
        }
        let days = a2_month::days_inmonth(year, month);
        if day == 0 || day > days {
            Err(format!("DateFT::from_ymd: day range must be [1, {days}] in month {year}-{month:0>2}"))?;
        }
        Ok(Self { year, month, day })

    }
    /// please maker sure: month∈[1, 12] && day∈[1, days_inmonth];
    /// in leap year: days_inmonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
    /// in non-leap year: days_inmonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    /// otherwise, this function is unsafe.
    pub fn from_ymd_unsafe(year: i32, month: u8, day: u8,) -> Self {
        let month_adjust = if month == 0 || month > 12 {
            1
        } else {
            month
        };
        let days = a2_month::days_inmonth(year, month);
        let day_adjust = if day == 0 || day > days {
            1
        } else {
            day
        };
        Self { year, month: month_adjust, day: day_adjust }
    }
}


impl DateFT {
    pub fn year(&self) -> i32 {
        self.year
    }
    pub fn month(&self) -> u8 {
        self.month
    }
    pub fn day(&self) -> u8 {
        self.day
    }
    pub fn ymd_friendly(&self) -> i32 {
        self.year * 100_00 + self.month as i32 * 100 + self.day as i32
    }


    /// let (year, month, day) = date.ymd();
    pub fn ymd_i32(&self) -> (i32, i32, i32) {
        // let (year, month, day) = date.ymd();
        (self.year, self.month as i32, self.day as i32)
    }

    /// let (year, month, day) = date.ymd();
    pub fn ymd(&self) -> (i32, u8, u8) {
        // let (year, month, day) = date.ymd();
        (self.year, self.month, self.day)
    }

    pub fn next_day(&self) -> Self {
        let year = self.year;
        let month = self.month;
        let day = self.day;
        if day < 28 {
            return Self {
                year,
                month,
                day: day + 1,
            };
        }
        let days = a2_month::days_inmonth(year, month);
        if day < days {
            return Self {
                year,
                month,
                day: day + 1,
            };
        }
        if month < 12 {
            return Self {
                year,
                month: month + 1,
                day: 1,
            };
        }
        // month = 12;

        Self {
            year: year + 1,
            month: 1,
            day: 1,
        }
    }
}


impl DateFT {
    pub fn to_print_8(&self) -> String {
        format!("{}{:0>2}{:0>2}", self.year, self.month, self.day)
    }
    pub fn to_print_9(&self) -> String {
        format!("{}-{:0>2}{:0>2}", self.year, self.month, self.day)
    }
    pub fn to_print_10(&self) -> String {
        format!("{}-{:0>2}-{:0>2}", self.year, self.month, self.day)
    }
    pub fn to_print_cn(&self) -> String {
        format!("{}年{:0>2}月{:0>2}日", self.year, self.month, self.day)
    }
}
impl std::fmt::Debug for DateFT {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // f.debug_struct("DateFT").field("print", &self.to_print_10()).field("year", &self.year).field("month", &self.month).field("day", &self.day).finish()
        write!(f, "{}", self.to_print_10())
    }
}

impl std::fmt::Display for DateFT {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.to_print_10())
    }
}
impl DateFT {
    pub fn to_le_bytes(&self) -> Vec<u8> {
        let mut bts = self.year.to_le_bytes().to_vec();
        bts.push(self.month);
        bts.push(self.day);
        bts
    }
    pub fn from_le_bytes(bts: Vec<u8>) -> IResult<Self> {
        if bts.len() != 6 {
            return Err(format!("DateFT::from_le_bytes: bts.len() != 6"))?;
        }
        let bts_year = [bts[0], bts[1], bts[2], bts[3]];
        let year = i32::from_le_bytes(bts_year);
        Self::from_ymd(year, bts[4], bts[5])
    }
}

impl DateFT {
    pub fn index_inyear(&self) -> usize {
        b1_date_utils::index_inyear(self)
    }
    pub fn days_inyear(&self) -> usize {
        b1_date_utils::days_inyear(self)
    }
}

impl DateFT {
    pub fn add_days(&self, days: i32) -> Self {
        // test_DateFT_add_days_20231104
        if days == 0 {
            return self.clone();
        }
        let mut year = self.year;

        let list_month_day_leap = a2_month::LIST_MONTH_DAY_LEAP.deref();
        let list_month_day_nonleap = a2_month::LIST_MONTH_DAY_NONLEAP.deref();
        if days > 0 {
            let index_inyear = self.index_inyear();
            let days_inyear = if year.is_leap() {
                let days_inyear = list_month_day_leap.len();
                let index = index_inyear + days as usize;
                if index < days_inyear {
                    let (month, day) = list_month_day_leap[index];
                    return Self { year, month, day };
                }
                days_inyear 
            } else {
                let days_inyear = list_month_day_nonleap.len();
                let index = index_inyear + days as usize;
                if index < days_inyear {
                    let (month, day) = list_month_day_nonleap[index];
                    return Self { year, month, day };
                }
                days_inyear
            };
            let mut days_reamin = days as usize - (days_inyear - index_inyear);
            year += 1;
            loop {
                if year.is_leap() {
                    let days_inyear = list_month_day_leap.len();
                    if days_reamin < days_inyear {
                        let (month, day) = list_month_day_leap[days_reamin];
                        return Self { year, month, day };
                    }
                    days_reamin -= days_inyear;
                    year += 1;
                } else {
                    let days_inyear = list_month_day_nonleap.len();
                    if days_reamin < days_inyear {
                        let (month, day) = list_month_day_nonleap[days_reamin];
                        return Self { year, month, day };
                    }
                    days_reamin -= days_inyear;
                    year += 1;
                }
            }
        }
        // if days < 0 {
        let index_inyear = self.index_inyear();
        let mut days_remain = days + index_inyear as i32;
        
        loop {
            if days_remain >= 0 {
                let (month, day) = if year.is_leap() {
                    list_month_day_leap[days_remain as usize]
                } else {
                    list_month_day_nonleap[days_remain as usize]
                };
                return Self { year, month, day };
            }
            year -= 1;
            days_remain += year.days();
        }


    }
}


#[test]
fn test_DateFT_1_20230918() {
    println!("1. DateFT::from_ymd OK...");
    match DateFT::from_ymd(2023, 9, 18) {
        Ok(v) => println!("\t{}\t{v:?}\t{}\t{}\t{}", v.to_print_8(), v.to_print_9(), v.to_print_10(), v.to_print_cn()),
        Err(_) => panic!(),
    }
    println!("2. DateFT::from_ymd fail month...");
    let year = 2023; let month = 19; let day = 58;
    match DateFT::from_ymd(year, month, day) {
        Ok(_) => (),
        Err(e) => println!("\t{year}-{month}-{day}, error: {e:?}"),
    }
    println!("3. DateFT::from_ymd fail day...");
    let year = 2023; let month = 9; let day = 58;
    match DateFT::from_ymd(year, month, day) {
        Ok(_) => (),
        Err(e) => println!("\t{year}-{month}-{day}, error: {e:?}"),
    }
    println!("4. DateFT::from_ymd leap or unleap year...");
    for (year, month, day) in vec![(2012, 2, 29), (2023, 2, 29), (1900, 2, 29), (1600, 2, 29)] {
        match DateFT::from_ymd(year, month, day) {
            Ok(v) => println!("\tOK leap year: {}", v.to_print_9()),
            Err(e) => println!("\tFail unleap year: {year}-{month}-{day}: {e:?}"),
        }
    }
    println!("5. DateFT::from_ymd to_le_bytes and from_le_bytes...");
    let date1 = DateFT { year: 1, month: 2, day: 3 };
    let bts = date1.to_le_bytes();
    let date2 = DateFT::from_le_bytes(bts).expect("Unreachable");
    println!("\t{date2:?}");


}


#[test]
fn test_DateFT_next_20231022() {
    let date_start = DateFT::from_ymd_unsafe(2023, 1, 1);
    let mut date = date_start.clone();
    println!("\t1: {}", date.to_print_9());
    for i in 2..367 {
        date = date.next_day();
        println!("\t{i}: {}", date.to_print_9());
    }
    
    let date_start = DateFT::from_ymd_unsafe(2024, 1, 1);
    let mut date = date_start.clone();
    println!("\t1: {}", date.to_print_9());
    for i in 2..368 {
        date = date.next_day();
        println!("\t{i}: {}", date.to_print_9());
    }
}
#[test]
fn test_DateFT_add_days_20231104() {
    let date_min = DateFT { year: 1915, month: 3, day: 31 };
    let date_mid = DateFT { year: 1970, month: 1, day: 1 };
    let mut buf = "测试开始: b1_date.rs::test_DateFT_add_days_20231104！\n".to_string();
    buf.push_str(&format!("【前半部分】test_DateFT_add_days_20231104: 最早日期={date_min}, 中间日期={date_mid}\n"));
    let len = 2000;
    for i in 0..len {
        let days_before_1 = i * 10;
        let date_min_新 = date_min.add_days(days_before_1);
        let days_before_2 = (i - len) * 10;
        let date_mid_新 = date_mid.add_days(days_before_2);
        buf.push_str(&format!("\t{date_min} + {days_before_1: >5} 天 = {date_min_新}; {date_mid} + {days_before_2: >5} 天 = {date_mid_新};\n"));
        assert_eq!(date_min_新, date_mid_新);
    }
    buf.push_str(&format!("【后半部分】test_DateFT_add_20231104: 最早日期={date_min}, 中间日期={date_mid}\n"));
    for i in 0..len {
        let days_before_1 = (i + len) * 10;
        let date_min_新 = date_min.add_days(days_before_1);
        let days_before_2 = i * 10;
        let date_mid_新 = date_mid.add_days(days_before_2);
        buf.push_str(&format!("\t{date_min} + {days_before_1: >5} 天 = {date_min_新}; {date_mid} + {days_before_2: >5} 天 = {date_mid_新};\n"));
        assert_eq!(date_min_新, date_mid_新);
    }
    crate::test_suites::write_utf8("./test_results/test_DateFT_add_20231104.txt", buf);

}